home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Applications / NIH Image 1.55 / Source / Registration.p < prev    next >
Encoding:
Text File  |  1994-05-03  |  28.8 KB  |  538 lines  |  [TEXT/PJMM]

  1.                     *}
  2. {*     e-mail address: mike.castle@med.umich.edu                                                                                       *}
  3. {*     last modified on 11 March 1994                                                                                                          *}
  4. {************************************************************************}
  5.  
  6. interface
  7.  
  8.     uses
  9.         QuickDraw, PrintTraps, Palettes, Globals, Utilities, File2, File1, Graphics, Camera, Text, Filters, Stacks;
  10.  
  11.  
  12.     procedure DoRegister;
  13.  
  14.  
  15. implementation
  16.  
  17.     const
  18.  
  19.         RegisterImagesID = 129;                         {Dialog IDs}
  20.         FiducialsOnScreenID = 3;
  21.         FiducialsFromFileID = 4;
  22.         ConfirmFidClicksID = 5;
  23.  
  24.         biggestFid = 9999;                                   {maximum allowable fiducial stage coordinate}
  25.         MaxFids = 12;                                           {maximum number of fiducial points per slice}
  26.         MaxRegSlices = 250;
  27.  
  28.     type
  29.         RegisterRealArray = array[1..MaxRegSlices] of real;
  30.         RealPoint = record
  31.                 x: real;
  32.                 y: real;
  33.             end;     {record}
  34.         FidArray = array[1..MaxRegSlices, 1..MaxFids] of RealPoint;
  35.  
  36.  
  37. {******************************************************************************}
  38. {*     RotateAboutXY rotates the point (x,y) counterclockwise by 'angle' radians about the point (xcenter,           *}
  39. {*  ycenter).                                                                                                                                                                *}
  40. {******************************************************************************}
  41.     procedure RotateAboutXY (var x, y: real; angle: real; xcenter, ycenter: real);
  42.         var
  43.             x0, y0: real;
  44.             SinAngle, CosAngle: real;
  45.     begin
  46.         x0 := x;
  47.         y0 := y;
  48.         CosAngle := cos(angle);
  49.         SinAngle := sin(angle);
  50.         x := (x0 - xcenter) * CosAngle - (y0 - ycenter) * SinAngle + xcenter;
  51.         y := (y0 - ycenter) * CosAngle + (x0 - xcenter) * SinAngle + ycenter;
  52.     end;     {RotateAboutXY}
  53.  
  54.  
  55. {******************************************************************************}
  56. {*     Read from a file the fiducial data necessary to register a set of images.  The data file contains several lines *}
  57. {*  of coordinates delimited by tabs (x-coordinate followed by y-coordinate in all cases).  The first line of the   *}
  58. {*  file should hold the coordinates of the Image Center point, the location (in screen coordinates) of the             *}
  59. {*  microscope crosshairs as they appear on the screen during image capture.  The second line of the file should *}
  60. {*  give the location in screen coordinates of two fixed points in an image (under the camera and microscope       *}
  61. {*  conditions selected for capture of the set of images to be aligned).  The third line of the file should provide    *}
  62. {*  the location of these two fixed points in microscope stage coordinates.  Each subsequent line in the file           *}
  63. {*  should contain (in stage coordinates) the locations of the Image Center and at least two fiducial points for an  *}
  64. {*  image in the set to be registered.  Each image must be represented by the same number of fiducial points in   *}
  65. {*  the data file.  Where fiducial coordinates are unavailable, coordinates of biggestFid+1 should appear.  No      *}
  66. {*  more than MaxFids fiducial points are allowed for each image.                                                                           *}
  67. {******************************************************************************}
  68.     function GetFiducialDataFromFile (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: real; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  69.         var
  70.             fiducialfname, str: str255;
  71.             RefNum, nValues, i, j, nImages: integer;
  72.             rLine: RealLine;
  73.     begin
  74.         nImages := info^.StackInfo^.nSlices;
  75.         GetFiducialDataFromFile := FALSE;
  76.         ShowMessage('Please open a fiducial data file.');
  77.         if not GetTextFile(fiducialfname, RefNum) then
  78.             exit(GetFiducialDataFromFile);
  79.         InitTextInput(fiducialfname, RefNum);
  80.         GetLineFromText(rLine, nValues);
  81.         if (nValues <> 2) then begin
  82.                 PutMessage('Expecting screen coordinates of Image Center point in line 1.  Please edit fiducial data file and try again.');
  83.                 exit(GetFiducialDataFromFile);
  84.             end;
  85.         xcscreen := round(rLine[1]);
  86.         ycscreen := round(rLine[2]);
  87.         GetLineFromText(rLine, nValues);
  88.         if (nValues <> 4) then begin
  89.                 PutMessage('Expecting screen coordinates of two points in line 2.  Please edit fiducial data file and try again.');
  90.                 exit(GetFiducialDataFromFile);
  91.             end;
  92.         xscreen1 := round(rLine[1]);
  93.         yscreen1 := round(rLine[2]);
  94.         xscreen2 := round(rLine[3]);
  95.         yscreen2 := round(rLine[4]);
  96.         GetLineFromText(rLine, nValues);
  97.         if (nValues <> 4) then begin
  98.                 PutMessage('Expecting stage coordinates of two points in line 3.  Please edit fiducial data file and try again.');
  99.                 exit(GetFiducialDataFromFile);
  100.             end;
  101.         xstage1 := rLine[1];
  102.         ystage1 := rLine[2];
  103.         xstage2 := rLine[3];
  104.         ystage2 := rLine[4];
  105.         i := 1;
  106.         GetLineFromText(rLine, nValues);
  107.         while (nvalues > 0) do begin
  108.                 if nValues >= 6 then begin
  109.                         for j := 1 to (nvalues - 2) div 2 do begin
  110.                                 fiducials[i, j].x := rLine[j * 2 + 1];
  111.                                 fiducials[i, j].y := rLine[j * 2 + 2];
  112.                             end;
  113.                         for j := (nvalues - 2) div 2 + 1 to MaxFids do begin
  114.                                 fiducials[i, j].x := biggestFid + 1;
  115.                                 fiducials[i, j].y := biggestFid + 1;
  116.                             end;     {for j}
  117.                         xc[i] := rLine[1];
  118.                         yc[i] := rLine[2];
  119.                     end
  120.                 else begin
  121.                         str := StringOf('Expecting coordinates of image center and at least two fiducial points in line ', (i + 3) : 1, '.  Please edit fiducial data file and try again.');
  122.                         PutMessage(str);
  123.                         exit(GetFiducialDataFromFile);
  124.                     end;
  125.                 i := i + 1;
  126.                 GetLineFromText(rLine, nValues);
  127.             end;     {while}
  128.         if (i < (nImages + 1)) then begin
  129.                 if (i = nImages) then
  130.                     str := StringOf('Expecting fiducial data for one more image.  Please edit fiducial data file, then try again. ')
  131.                 else
  132.                     str := StringOf('Expecting fiducial data for ', (nImages + 1 - i) : 1, ' more slices.  Please edit fiducial data file, then try again. ');
  133.                 PutMessage(str);
  134.                 exit(GetFiducialDataFromFile);
  135.             end;
  136.         if (i > (nImages + 1)) then begin
  137.                 if (i = nImages + 2) then
  138.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ')
  139.                 else
  140.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ');
  141.                 PutMessage(str);
  142.                 exit(GetFiducialDataFromFile);
  143.             end;
  144.         GetFiducialDataFromFile := TRUE;
  145.     end;     {GetFiducialDataFromFile}
  146.  
  147.  
  148. {******************************************************************************}
  149. {*     Read the coordinates of a fiducial point entered on the screen by clicking once with the mouse.  Interpret    *}
  150. {*  a double-click to indicate that this is the last fiducial point for the current slice, a spacebar-click to          *}
  151. {*  to indicate that no valid fiducial exists corresponding to this fiducial in other slices (record BiggestFid+1   *}
  152. {*  as value for each coordinate), an option-click to indicate that some data for this slice has been improperly  *}
  153. {*  and the user would like to re-enter all fiducials for this slice, and command-period to indicate that the       *}
  154. {*  user wishes to cancel image registration altogether, discarding all entered fiducials coordinates.                    *}
  155. {******************************************************************************}
  156.     function GetNextFiducial (var Fidx, Fidy: integer; var done, redo: boolean): boolean;
  157.         var
  158.             pt1, pt2: point;
  159.             sbdown, optdown, DoubleClick: boolean;
  160.             MouseUpTime: LongInt;
  161.     begin
  162.         SetCursor(ToolCursor[SelectionTool]);
  163.         GetNextFiducial := FALSE;
  164.         repeat
  165.             sbdown := SpaceBarDown;
  166.             optdown := OptionKeyDown;
  167.             SetPort(info^.wptr);
  168.             GetMouse(pt1);
  169.             Show3Values(pt1.h, pt1.v, MyGetPixel(pt1.h, pt1.v));
  170.             if CommandPeriod then begin
  171.                     ShowMessage('Fiducial input cancelled.');
  172.                     exit(GetNextFiducial);
  173.                 end;     {then}
  174.         until button;
  175.         repeat
  176.         until not (button);
  177.         MouseUpTime := TickCount;
  178.         DoubleClick := FALSE;
  179.         repeat
  180.             GetMouse(pt2);
  181.             if EqualPt(pt1, pt2) then
  182.                 DoubleClick := button;
  183.         until (TickCount - MouseUpTime > GetDblTime) or DoubleClick;
  184.         if sbdown then begin
  185.                 Fidx := BiggestFid + 1;
  186.                 Fidy := BiggestFid + 1;
  187.             end
  188.         else if optdown then
  189.             redo := TRUE
  190.         else begin
  191.                 Fidx := pt1.h;
  192.                 Fidy := (Info^.nLines - 1) - pt1.v;
  193.             end;
  194.         done := DoubleClick;
  195.         while (button) do                    {clear out any buffered mouse clicks;  I don't know why there would be any}
  196.             ;                                               {such clicks, but they can be *very* disruptive while running on Quadras!}
  197.         FlushEvents(62, 0);                {make sure clicks and key presses don't linger in the event queue after exit}
  198.         GetNextFiducial := TRUE;
  199.     end;     {GetNextFiducial}
  200.  
  201.  
  202.     procedure SetSlice (i: integer);
  203.     begin
  204.         SelectSlice(i);
  205.         Info^.StackInfo^.CurrentSlice := i;
  206.         UpdateTitleBar;
  207.     end;
  208.  
  209.  
  210. {******************************************************************************}
  211. {*     Read fiducial coordinates for a set of slices to be placed in register with a reference slice (a member of   *}
  212. {*  the set).  Begin by reading fiducials for the reference slice, then read the rest.  Assign dummy values to      *}
  213. {*  variables in the fiducial data structure whose values would be used for mapping between two coordinate        *}
  214. {*  systems had fiducial data been read from a file.                                                                                                   *}
  215. {******************************************************************************}
  216.     function GetFiducialDataFromScreen (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: real; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  217.         var
  218.             i, j: integer;
  219.             nImages: integer;                               {number of slices}
  220.             RefSlice: integer;                               {index of the reference slice}
  221.             Fidx, Fidy: integer;                            {coordinates of selected fiducial point}
  222.             done: boolean;                                     {done entering fiducials for this slice}
  223.             redo: boolean;                                     {re-enter fiducials for this slice}
  224.             str: str255;
  225.     begin
  226.         GetFiducialDataFromScreen := FALSE;
  227.         nImages := Info^.StackInfo^.nSlices;
  228.         xcscreen := 100;                                 {arbitrarily assign dummy image center}
  229.         ycscreen := 100;
  230.         xscreen1 := 50;                                   {assign screen and stage coords of two points}
  231.         yscreen1 := 50;                                   {so that mapping is 1:1 in x and y}
  232.         xscreen2 := 80;
  233.         yscreen2 := 80;
  234.         xstage1 := 50.0;
  235.         ystage1 := 50.0;
  236.         xstage2 := 80.0;
  237.         ystage2 := 80.0;
  238.         DrawLabels('X: ', 'Y: ', 'Value: ');        {prepare to show x,y values in results window}
  239.         RefSlice := info^.StackInfo^.CurrentSlice;
  240.         i := RefSlice;                                   {begin with reference slice}
  241.         while (i <= nImages) do begin
  242.                 done := FALSE;
  243.                 redo := FALSE;
  244.                 SetSlice(i);
  245.                 UpdatePicWindow;
  246.                 for j := 1 to MaxFids do begin
  247.                         if (not done) and (not redo) then begin
  248.                                 str := StringOf('Click on fiducial point  ', j : 1);
  249.                                 showmessage(str);
  250.                                 if not GetNextFiducial(Fidx, Fidy, done, redo) then
  251.                                     exit(GetFiducialDataFromScreen);
  252.                                 if ConfirmFidClicks then begin
  253.                                         str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  254.                                         if not (redo) then
  255.                                             while (PutMessageWithCancel(str) = cancel) do begin
  256.                                                     UpdatePicWindow;
  257.                                                     if not GetNextFiducial(Fidx, Fidy, done, redo) then
  258.                                                         exit(GetFiducialDataFromScreen);
  259.                                                     str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  260.                                                 end;     {while}
  261.                                         UpdatePicWindow;
  262.                                     end;     {then}
  263.                                 if done and (j = 1) then begin
  264.                                         PutMessage('You must select at least two fiducial points in each slice for registration.');
  265.                                         redo := TRUE;
  266.                                     end;     {then}
  267.                                 fiducials[i, j].x := Fidx;
  268.                                 fiducials[i, j].y := Fidy;
  269.                             end    {then}
  270.                         else begin                                  {pad rest of array with invalid fiducial data}
  271.                                 fiducials[i, j].x := biggestFid + 1;
  272.                                 fiducials[i, j].y := biggestFid + 1;
  273.                             end;     {else}
  274.                     end;     {for j}
  275.                 xc[i] := 100;
  276.                 yc[i] := 100;
  277.                 if not (redo) then begin
  278.                         if (i = RefSlice) and (RefSlice <> 1) then
  279.                             i := 1
  280.                         else if (i = RefSlice - 1) then  {don't read fiducials from reference slice twice!}
  281.                             i := i + 2
  282.                         else
  283.                             i := i + 1;
  284.                     end     {then}
  285.                 else
  286.                     PutMessage('Input cancelled.  Please reselect fiducial points for this slice.');
  287.             end;     {while}
  288.         GetFiducialDataFromScreen := TRUE;
  289.     end;     {GetFiducialDataFromScreen}
  290.  
  291.  
  292. {******************************************************************************}
  293. {*     Before rotating and translating an image into register, center it in a larger buffer so that rotation does      *}
  294. {*  not unnecessarily clip portions of the image that will return to view after translation.                                   *}
  295. {******************************************************************************}
  296.     procedure CenterInBigBuffer (i, picwidth, picheight, bigpicwidth, bigpicheight: integer; StackInfo: InfoPtr);
  297.         var
  298.             vloc, hOffset, vOffset: integer;
  299.             BigBufInfo: InfoPtr;
  300.             aLine: LineType;
  301.     begin
  302.         BigBufInfo := info;
  303.         info := StackInfo;
  304.         SetSlice(i);
  305.         info := BigBufInfo;
  306.         SetForegroundColor(BlackIndex);
  307.         SetBackgroundColor(WhiteIndex);
  308.         SelectAll(false);
  309.         DoOperation(EraseOp);
  310.       {write image one line at a time to center of larger buffer}
  311.         hOffset := (bigpicwidth - picwidth) div 2;
  312.         vOffset := (bigpicheight - picheight) div 2;
  313.         for vloc := 0 to picheight - 1 do begin
  314.                 info := StackInfo;
  315.                 GetLine(0, vloc, picwidth, aLine);
  316.                 info := BigBufInfo;
  317.                 PutLine(hOffset, vloc + vOffset, picwidth, aLine);
  318.             end;
  319.         UpdatePicWindow;
  320.     end;     {RegisterCenterInBigBuffer}
  321.  
  322.  
  323. {******************************************************************************}
  324. {*     After registration is complete, move the centered image back to its original window size.                             *}
  325. {******************************************************************************}
  326.     procedure TranslateBackToStack (i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight: longint; StackInfo: InfoPtr);
  327.         var
  328.             vloc, hoffset, voffset: integer;
  329.             offset: longint;
  330.             BigBufInfo: InfoPtr;
  331.             aLine: LineType;
  332.     begin
  333.         BigBufInfo := info;
  334.         info := StackInfo;
  335.         SetSlice(i);
  336.         hOffset := (bigpicwidth - picwidth) div 2 - xdelta;
  337.         vOffset := (bigpicheight - picheight) div 2 + ydelta;
  338.         for vloc := 0 to picheight - 1 do begin
  339.                 info := BigBufInfo;
  340.                 GetLine(hOffset, vloc + vOffset, picwidth, aLine);
  341.                 info := StackInfo;
  342.                 PutLine(0, vloc, picwidth, aLine);
  343.             end;
  344.         UpdatePicWindow;
  345.         info := BigBufInfo;
  346.     end;     {RegisterBackToSmallWindow}
  347.  
  348.  
  349. {******************************************************************************}
  350. {*     Find the angle which the current slice must be rotated in order to place it in register with the reference  *}
  351. {*  slice.  For corresponding pairs of fiducial points in the current and reference slices, use simple                 *}
  352. {*  trigonometry to find the angle between lines passing through each pair of points.  Take an everage of the        *}
  353. {*  angles found from each set of corresponding pairs of points to find the rotation angle.                                       *}
  354. {******************************************************************************}
  355.     function RegisterFindAngle (var fiducials: FidArray; Cur, Ref: integer): real;
  356.         var
  357.             j, k, n: integer;
  358.             angle, anglecur, angleref: real;
  359.             tancur, tanref: real;
  360.             sumangle: real;
  361.     begin
  362. {find average angle between current fiducial segments and reference fiducial segments}
  363.         sumangle := 0;
  364.         n := 0;
  365.         for j := 1 to MaxFids - 1 do
  366.             for k := j + 1 to MaxFids do
  367.                 if (j <> k) and (fiducials[Cur, j].x < biggestFid) and (fiducials[Cur, j].y < biggestF*****************************************************************}
  368. {*     This procedure allows the user to determine, via radio buttons in a dialog box, whether fiducial data           *}
  369. {*  will be read from the screen or from a file.  The dialog box also contains details about how to select fiducial   *}
  370. {*  points on the screen.                                                                                                                                              *}
  371. {******************************************************************************}
  372.     function RegisterOptions: boolean;
  373.         var
  374.             mylog: Dialogptr;                                           {pointer to dialog box}
  375.             i, item, alldone: integer;
  376.             SaveFiducialMethod: FiducialMethodType;
  377.             SaveConfirmFidClicks: boolean;
  378.     begin
  379.         RegisterOptions := FALSE;
  380.         InitCursor;
  381.         SaveConfirmFidClicks := ConfirmFidClicks;
  382.         SaveFiducialMethod := FiducialMethod;
  383.         mylog := GetNewDialog(RegisterImagesID, nil, pointer(-1));
  384.         OutlineButton(MyLog, ok, 16);
  385.         SetDialogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  386.         SetDialogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  387.         SetDialogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  388.         alldone := 0;
  389.         repeat  {if we don't do it this way, ModalDialog throws us into code checking after each keystroke}
  390.             repeat   {meaning you can't type in a 2 digit number}
  391.                 ModalDialog(nil, item);
  392.                 if item = ConfirmFidClicksID then begin
  393.                         ConfirmFidClicks := not ConfirmFidClicks;
  394.                         SetDialogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  395.                     end;
  396.                 if (item = FiducialsOnScreenID) or (item = FiducialsFromFileID) then begin
  397.                         case item of
  398.                             FiducialsOnScreenID: 
  399.                                 FiducialMethod := OnScreen;
  400.                             FiducialsFromFileID: 
  401.                                 FiducialMethod := FromFile;
  402.                         end;     {case}
  403.                         SetDialogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  404.                         SetDialogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  405.                     end;
  406.             until (item = ok) or (item = cancel);
  407.             alldone := 1;
  408.         until (alldone = 1);
  409.         DisposDialog(mylog);
  410.         if item = cancel then begin                             {if Cancel, keep the saved values}
  411.                 FiducialMethod := SaveFiducialMethod;
  412.                 ConfirmFidClicks := SaveConfirmFidClicks;
  413.                 Exit(RegisterOptions);
  414.             end;
  415.         RegisterOptions := TRUE;
  416.     end;
  417.  
  418.  
  419.  
  420. {******************************************************************************}
  421. {*     Place a set of slices in register with a reference slice using fiducial marks.  All slices in the stack              *}
  422. {*  are placed in register with the current slice using fiducial data gathered either from a text file or                 *}
  423. {*  from the user's mouse clicks on the screen.                                                                                                         *}
  424. {******************************************************************************}
  425.     procedure DoRegister;
  426.         var
  427.             nImages: integer;                                                     {total number of open slices to register}
  428.             RefImage: integer;                                                       {the index of the reference slice}
  429.             bigpicwidth, bigpicheight: longint;                        {width,height of big buffer used for rotation and translation}
  430.             picwidth, picheight: integer;                                  {width,height of slices to register}
  431.             xcenter, ycenter: integer;                                      {coordinates of center of big, temp window}
  432.             xdelta, ydelta: integer;                                            {translation offsets}
  433.             xcscreen, ycscreen: integer;                                  {image center on the screen}
  434.             xscreen1, yscreen1, xscreen2, yscreen2: integer;{two points in screen coordinates}
  435.             xstage1, ystage1, xstage2, ystage2: real;              {same two points in stage coordinates}
  436.             xscale, yscale: real;                                               {used for mapping stage to screen coords}
  437.             xc, yc: RegisterRealArray;                                    {array of image centers in stage coords}
  438.             fiducials: FidArray;                                                {array of fiducial point data for all slices}
  439.             xcenterStage, ycenterStage: real;                          {used in translation calculation}
  440.             angle: real;                                                             {mean angle between ref and cur fid segments}
  441.             i, ignore: integer;                                                   {loop indices and temp var}
  442.             TimeStr, seconds: str255;
  443.             StartTicks, TicksForOneRegistration, TicksToGo: LongInt;
  444.             StackInfo: InfoPtr;
  445.             SlicesDone: integer;
  446.  
  447.     begin
  448.         if not (RegisterOptions) then
  449.             exit(DoRegister);
  450.         with Info^ do begin
  451.                 picwidth := PixelsPerLine;
  452.                 picheight := nLines;
  453.                 RefImage := StackInfo^.CurrentSlice;
  454.                 nImages := StackInfo^.nSlices;
  455.             end;
  456.         if nImages > MaxRegSlices then begin
  457.                 PutMessage(concat('Unable to register more than ', long2str(MaxRegSlices), ' slices.'));
  458.                 exit(DoRegister);
  459.             end;
  460.         StackInfo := info;
  461.         bigpicwidth := 2 * (round(1.414 * picwidth) div 2);      {allow for image rotation without losing corners}
  462.         bigpicheight := 2 * (round(1.414 * picheight) div 2);    {odd window dims mysteriously don't work}
  463.         if (bigpicwidth * bigpicheight) > UndoBufSize then begin
  464.                 PutMessage(concat('To register this stack, the size of the Undo buffer must be increased to at least ', long2str(bigpicwidth * bigpicheight div 1024), 'K.'));
  465.                 exit(DoRegister);
  466.             end;
  467.         xcenter := bigpicwidth div 2;                                              {find center}
  468.         ycenter := bigpicheight div 2;
  469. {open fiducial data file}
  470. {read fiducial marks and image centers into arrays; RegPic is image reference}
  471. {get screen to stage coordinate mapping}
  472.         case FiducialMethod of
  473.             OnScreen: 
  474.                 if not GetFiducialDataFromScreen(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  475.                     exit(DoRegister);
  476.             FromFile: 
  477.                 if not GetFiducialDataFromFile(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  478.                     exit(DoRegister)
  479.         end;     {case}
  480.         xscale := (xscreen2 - xscreen1) / (xstage2 - xstage1);
  481.         yscale := (yscreen2 - yscreen1) / (ystage2 - ystage1);
  482.         xcscreen := xcscreen + (bigpicwidth - picwidth) div 2;     {adjust for bigger window}
  483.         ycscreen := ycscreen + (bigpicheight - picheight) div 2;
  484.         xcenterStage := (xcenter - xcscreen) / xscale;
  485.         ycenterStage := (ycenter - ycscreen) / yscale;
  486.         UpdatePicWindow;
  487.         ShowWatch;
  488.         i := 1;
  489.         if not NewPicWindow('Temp', bigpicwidth, bigpicheight) then
  490.             exit(DoRegister);
  491.         ShowMessage(CmdPeriodToStop);
  492.         SlicesDone := 1; {Don't need to process reference slice}
  493.         while (i <= nImages) and not CommandPeriod do begin
  494.                 if i = RefImage then begin
  495.                         i := i + 1;
  496.                         if i > nImages then
  497.                             leave;
  498.                     end;
  499.                 StartTicks := TickCount;
  500.                 with info^ do
  501.                     SetWTitle(wptr, concat('Temp (', long2str(i), '/', long2str(nImages), ')'));
  502.         {rotate image then translate to complete registration}
  503.                 angle := RegisterFindAngle(fiducials, i, RefImage);
  504.                 if (angle > 9999) then
  505.                     leave;
  506.                 CenterInBigBuffer(i, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  507.                 if (abs(angle) > 0.0001) then
  508.                     RegisterRotate(angle);
  509.                 if CommandPeriod then
  510.                     leave;
  511.                 FindTranslation(xdelta, ydelta, fiducials, i, RefImage, xscale, yscale, angle, xc, yc, xcenterStage, ycenterStage);
  512.                 TranslateBackToStack(i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  513.                 SlicesDone := SlicesDone + 1;
  514.                 TicksForOneRegistration := TickCount - StartTicks;
  515.                 TicksToGo := (nImages - SlicesDone) * TicksForOneRegistration;
  516.                 NumToString((TicksToGo div 60) mod 60, seconds);
  517.                 if length(seconds) = 1 then
  518.                     seconds := concat('0', seconds);
  519.                 timestr := concat(long2str(TicksToGo div 3600), ':', seconds);
  520.                 ShowMessage(concat(CmdPeriodToStop, cr, 'time: ', timestr));
  521.                 i := i + 1;
  522.             end;     {while i}
  523.         Info^.changes := false;
  524.         ignore := CloseAWindow(info^.wptr);
  525.         info := StackInfo;
  526.         if CommandPeriod then
  527.             ShowMessage('Registration cancelled.')
  528.         else begin
  529.                 SetSlice(RefImage);  {select registered slice as current slice}
  530.                 UpdatePicWindow;
  531.                 info^.changes := true;
  532.                 ShowMessage('Registration successful.');
  533.             end;     {else}
  534.     end;
  535.  
  536.  
  537.  
  538. end.